package com.zhangdi.zddemo.esclient.repository;

import com.zhangdi.zddemo.esclient.MSearchRequest;
import com.zhangdi.zddemo.esclient.enums.MatchType;
import com.zhangdi.zddemo.esclient.page.Page;
import com.zhangdi.zddemo.esclient.page.Pageable;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.stereotype.Component;
import sun.reflect.generics.reflectiveObjects.NotImplementedException;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.*;

@Component
public class ClientBase<C extends MSearchRequest, T> extends RestHighLevelClient implements ClientApi<C, T> {

    public ClientBase(RestClientBuilder mClientBuilder) {
        super(mClientBuilder);
    }

    @Override
    public Page<T> search(Pageable<List<MSearchRequest>> pageable, Class<T> tClass) {
        Page<T> pager = new Page<>();

        List<T> result = new ArrayList<>();
        List<NestedQueryBuilder> nestedQueryBuilderList = new ArrayList<>();
        List<QueryBuilder> parentQueryBuilderList = new ArrayList<>();
        StringBuffer sb = new StringBuffer();
        List<MSearchRequest> c = pageable.getT();
        for (MSearchRequest request : c) {
            if (request.getPath() == null) {
                parentQueryBuilderList.add(this.builder(request));
                continue;
            }
            sb.setLength(0);
            sb.append(request.getPath());
            sb.append(".");
            sb.append(request.getParam());
            NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery(request.getPath(),
                    this.builder(request, sb.toString()),
                    ScoreMode.Max);
            nestedQueryBuilderList.add(nestedQueryBuilder);
        }

        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        for (NestedQueryBuilder qbuilder : nestedQueryBuilderList) {
            boolQuery.must(qbuilder);
        }
        for (QueryBuilder qbuilder : parentQueryBuilderList) {
            boolQuery.must(qbuilder);
        }
        SearchSourceBuilder builder = new SearchSourceBuilder();
        builder.query(boolQuery);
        if(pageable.getNo() == 0){
            pageable.setNo(1);
        }
        pageable.setSize(20);
        builder.from((pageable.getNo() - 1) * pageable.getSize());
        builder.size(pageable.getSize());

        SearchRequest searchRequest = new SearchRequest();
        searchRequest.source(builder);
        try {
            SearchResponse response = super.search(searchRequest, RequestOptions.DEFAULT);

            SearchHits hits = response.getHits();
            pager.setTotal((int)hits.getTotalHits().value);
            for (SearchHit hit : hits) {
                Map<String, Object> map = hit.getSourceAsMap();
                T t = tClass.getConstructor().newInstance();
                BeanUtils.populate(t, map);
                result.add(t);
            }
        } catch (IOException | NoSuchMethodException | InstantiationException
                | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }

        pager.setNo(pageable.getNo());
        pager.setSize(pageable.getSize());
        pager.setList(result);

        return pager;
    }

    @Override
    public List<T> search(List<MSearchRequest> pageable, Class<T> tClass) {
        throw new NotImplementedException();
    }

    @Override
    public List<T> search(MSearchRequest c) {
        throw new NotImplementedException();

    }

    private QueryBuilder builder(MSearchRequest request, String childKeyName) {
        QueryBuilder queryBuilder = null;
        if (request.getMatchType() == MatchType.match) {
            queryBuilder = QueryBuilders.matchQuery(childKeyName, request.getValue());
        } else if (request.getMatchType() == MatchType.match_phase) {
            queryBuilder = QueryBuilders.matchPhraseQuery(childKeyName, request.getValue());
        } else if (request.getMatchType() == MatchType.term) {
            queryBuilder = QueryBuilders.termQuery(childKeyName, request.getValue());
        }
        return queryBuilder;
    }

    private QueryBuilder builder(MSearchRequest request) {
        return this.builder(request, request.getParam());
    }
}
